package com.sunday.common.mq.rabbit.study.base.e11_LazyQueues;

import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;
import com.rabbitmq.client.ConnectionFactory;
import com.sunday.common.mq.rabbit.study.base.ConnectionUtil;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import org.junit.jupiter.api.Test;

import java.util.Collections;

/**
 * https://www.rabbitmq.com/lazy-queues.html
 * 懒惰队列
 * 等等，有更好的方法：下一代经典队列存储
 * 本指南介绍经典队列的延迟模式。
 * 仲裁队列是一种替代的、更现代的队列类型 通过复制提供高可用性，并专注于数据安全。 从 RabbitMQ 3.10 开始，仲裁队列支持消息 TTL 和 与镜像经典队列相比，提供更高的吞吐量和更稳定的延迟。
 * Streams 是从 RabbitMQ 3.9 开始提供的消息传递数据结构， 并且也被复制。
 * 仲裁队列应该是复制队列类型的默认选择。 经典队列镜像将在 RabbitMQ 的未来版本中删除： 经典队列仍将是受支持的非复制队列类型。
 * <p>
 * 概述
 * 经典队列可以在惰性模式下运行：即 尽可能早地将其内容移动到磁盘， 并且仅在消费者请求时才将它们加载到 RAM 中， 因此是懒惰的面额。
 * 惰性队列的主要目标之一是能够支持非常 长队列（数百万条消息）。队列可能会变得很长 出于各种原因：
 * <p>
 * 消费者离线/已崩溃/停机进行维护
 * 消息入口突然激增，生产商超过消费者
 * 消费者比正常情况慢
 * <p>
 * 默认情况下，队列会保留消息的内存缓存，该缓存在消息发布到 RabbitMQ 时被填满。 此缓存的想法是能够尽快将消息传递给消费者。 请注意，持久消息可以在进入代理时写入磁盘，并同时保存在 RAM 中。
 * 每当代理认为需要释放内存时，来自此缓存的消息将被分页到磁盘。 将一批消息分页到磁盘需要时间并阻塞队列进程， 使其在分页时无法接收新消息。
 * 尽管最新版本的 RabbitMQ 改进了分页算法， 对于您有以下情况的用例，情况仍然不理想 队列中可能需要分页的邮件数以百万计。
 * 延迟队列会尝试尽可能早地将消息移动到磁盘。 这意味着在大多数情况下，在正常操作下，RAM 中保留的消息要少得多。 这是以增加磁盘 I/O 为代价的。
 * <p>
 * 延迟队列的性能注意事项
 * 磁盘利用率
 * 延迟队列会尽快将其消息移动到磁盘，即使消息已发布 作为发布者的暂时性。这通常会导致更高的磁盘 I/O 利用率。
 * 常规队列会将消息保留在内存中更长时间。 这将导致延迟的磁盘 I/O，其不太均匀（具有更多的峰值） 因为需要一次将更多数据写入磁盘。
 * <p>
 * 在运行时切换队列模式
 * 将默认队列转换为惰性队列时， 对操作的性能影响与队列需要将消息分页到磁盘时的影响相同。
 * 在从常规模式转换为懒惰模式的过程中， 队列将首先将保存在 RAM 中的所有消息分页到磁盘。它不会再接受了 在该操作正在进行时来自发布频道的消息。 初始分页完成后，队列将开始接受发布、确认和其他命令。
 * 当队列从 lazy 模式变为 default 模式时， 它将执行与服务器重新启动后恢复队列时相同的过程： 一批 16384 条消息将加载到内存中。
 * <p>
 * 注意事项和限制
 * 当优先级保持较低的节点内存使用率时，延迟队列是合适的 更高的磁盘 I/O 和磁盘利用率是可以接受的。延迟队列还有其他方面 这应该被考虑。
 */
@Slf4j
public class LazyQueues {


    /**
     * 使队列变得懒惰
     * 可以通过以下方式使队列在 default 模式或 lazy 模式下运行：
     * 应用队列策略（推荐）
     * 通过队列设置模式。声明参数
     * 当策略和队列参数都指定队列模式时， 如果同时指定了 Queue 参数，则 Queue 参数优先于策略值。
     * 如果在声明时通过可选参数设置了队列模式， 它只能通过删除队列并在以后使用不同的参数重新声明来更改。
     * <p>
     * 在声明时使用参数
     * 可以通过向 x 队列模式队列声明参数提供指定所需模式的字符串来设置队列模式。 有效模式包括：
     * "default"
     * "lazy"
     * 如果在声明期间未指定模式，则假定为“default”。 default 模式是 3.6.0 之前版本的代理中已经存在的行为， 因此，在这方面没有重大变化。
     */
    @Test
    @SneakyThrows
    public void makingQueueLazy() {

        ConnectionFactory cf = ConnectionUtil.factory();
        try (Connection connection = cf.newConnection()) {
            Channel channel = connection.createChannel();

            channel.queueDelete("queue_length");
            channel.queueDelete("queue_length_bytes");

            /**
             * x-max-length 设置队列中可以存储处于ready状态消息的数量
             */
            channel.queueDeclare("queue_lazy", false, false, false, Collections.singletonMap("x-queue-mode", "lazy"));

        }
    }

}
